﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Tycho.Runtime;
using System.Windows.Controls.Primitives;
using System.Windows.Threading;
using Tycho.Transactions;

namespace Tycho.Peyote {
    /// <summary>
    /// Interaction logic for GridGlyph.xaml
    /// </summary>
    public partial class GridGlyph : UserControl {
        AnyObject Data;
        public Environment Environment { get; set; }
        List<List<TextBox>> Cells;

        class CellIndex {
            public int Row, Column;

            public CellIndex (int row, int column) {
                Row = row;
                Column = column;
            }

            public CellIndex (CellIndex cellIndex) {
                Row = cellIndex.Row;
                Column = cellIndex.Column;
            }
        }

        public GridGlyph () {
            InitializeComponent ();

            Data = GetData ();
        }

        AnyObject GetData () {
            AnyObject list = RuntimeModule.CreateList ();

            int n = 1;
            Symbol nameSymbol = Namespaces.User.Get ("name");
            Symbol numberSymbol = Namespaces.User.Get ("number");
            foreach (string name in new string [] { "snow", "flakes", "6ix" }) {
                list.Add (new StructureObject (nameSymbol, RuntimeModule.CreateString (name), numberSymbol, RuntimeModule.CreateInteger (n++)));
            }

            return new TransactionalObject (list);
        }

        void RefreshData () {
            TransactionalObjectManager.RunTransaction (delegate () {
                IEnumerable<AnyObject> columnNames = GetColumnNames ();
                SetupGrids (columnNames);

                int rowCount = Data.Count;
                for (int n = 0; n < rowCount; n++) {
                    RowDefinition row = new RowDefinition ();
                    row.Height = GridLength.Auto;
                    DataGrid.RowDefinitions.Add (row);
                }

                Cells = new List<List<TextBox>> ();

                int rowIndex = 1;
                foreach (AnyObject item in Data) {
                    Cells.Add (new List<TextBox> ());

                    int colIndex = 0;
                    foreach (AnyObject colName in columnNames) {
                        string contents = item [colName].ToString ();
                        TextBox cell = new TextBox ();
                        cell.Text = contents;
                        cell.Style = (Style) FindResource ("GridCell");

                        cell.PreviewKeyDown += new KeyEventHandler (Cell_KeyDown);

                        Cells [rowIndex - 1].Add (cell);
                        cell.Tag = new CellIndex (rowIndex - 1, colIndex);

                        Grid.SetColumn (cell, colIndex);
                        Grid.SetRow (cell, rowIndex);
                        DataGrid.Children.Add (cell);
                        colIndex++;
                    }
                    rowIndex++;
                }
            });
        }

        void Cell_KeyDown (object sender, KeyEventArgs e) {
            CellIndex index = new CellIndex ((CellIndex) ((FrameworkElement) sender).Tag);

            switch (e.Key) {
                case Key.Up:
                    index.Row--;
                    break;
                case Key.Down:
                    index.Row++;
                    break;
                default:
                    return;
            }

            FrameworkElement targetCell = GetCell (index.Row, index.Column);
            if (targetCell != null) {
                targetCell.Focus ();
            }

            e.Handled = true;
        }

        FrameworkElement GetCell (int row, int col) {
            if (row < Cells.Count && row >= 0) {
                var rowCells = Cells [row];

                if (col < rowCells.Count && col >= 0) {
                    return rowCells [col];
                } else {
                    return null;
                }
            } else {
                return null;
            }
        }

        void SetupGrids (IEnumerable<AnyObject> columnNames) {
            SetupDataGrid (DataGrid, columnNames);
            SetupDataGrid (NewRowsGrid, columnNames);
        }

        private void SetupDataGrid (Grid grid, IEnumerable<AnyObject> columnNames) {
            grid.Children.Clear ();
            grid.RowDefinitions.Clear ();
            grid.ColumnDefinitions.Clear ();

            int columnCount = columnNames.Count ();
            for (int n = 0; n < columnCount; n++) {
                ColumnDefinition col = new ColumnDefinition ();
                if (n != columnCount - 1) {
                    col.Width = GridLength.Auto;
                }
                grid.ColumnDefinitions.Add (col);
            }

            RowDefinition row = new RowDefinition ();
            row.Height = GridLength.Auto;
            grid.RowDefinitions.Add (row);

            int colHeaderIndex = 0;
            foreach (AnyObject colName in columnNames) {
                PutColumnHeader (grid, colName, colHeaderIndex, colHeaderIndex != columnCount - 1);

                colHeaderIndex++;
            }
        }

        void PutColumnHeader (Grid grid, AnyObject colName, int colIndex, bool withSplitter) {
            TextBlock header = new TextBlock (new Run (colName.ToString ()));
            header.Style = (Style) FindResource ("GridColumnHeader");
            Grid.SetColumn (header, colIndex);
            Grid.SetRow (header, 0);
            if (withSplitter) {
                header.Margin = new Thickness (0, 0, 5, 0);
            }
            grid.Children.Add (header);

            if (withSplitter) {
                GridSplitter splitter = new GridSplitter ();
                splitter.HorizontalAlignment = HorizontalAlignment.Right;
                splitter.VerticalAlignment = VerticalAlignment.Stretch;
                splitter.Width = 5;
                Grid.SetColumn (splitter, colIndex);
                Grid.SetRow (splitter, 0);
                grid.Children.Add (splitter);
            }
        }

        IEnumerable<AnyObject> GetColumnNames () {
            Dictionary<AnyObject, int> names = new Dictionary<AnyObject, int> ();

            foreach (AnyObject item in Data) {
                foreach (AnyObject prop in item.GetProperty (Symbols.RuntimeProperties)) {
                    if (prop is Symbol && prop.Expect<Symbol> ().Namespace == Environment.DefaultNamespace) {
                        if (names.ContainsKey (prop)) {
                            names [prop]++;
                        } else {
                            names.Add (prop, 1);
                        }
                    }
                }
            }

            int total = Data.Count;
            int minimumEntriesForColumn = (int) (total * 0.5);

            return names.Where (kv => kv.Value > minimumEntriesForColumn).Select (kv => kv.Key);
        }

        private void RefreshButton_Click (object sender, RoutedEventArgs e) {
            RefreshData ();
        }

        private void AddColumnButton_Click (object sender, RoutedEventArgs e) {
            DataGrid.ColumnDefinitions [DataGrid.ColumnDefinitions.Count - 1].Width = GridLength.Auto;
            DataGrid.ColumnDefinitions.Add (new ColumnDefinition ());

            PutColumnHeader (DataGrid, Environment.DefaultNamespace.Get ("something"), DataGrid.ColumnDefinitions.Count - 1, false);
        }

        private void AddRowButton_Click (object sender, RoutedEventArgs e) {
            NewRowsGrid.Visibility = Visibility.Visible;

            RowDefinition row = new RowDefinition ();
            row.Height = GridLength.Auto;
            NewRowsGrid.RowDefinitions.Add (row);
        }
    }
}
